home *** CD-ROM | disk | FTP | other *** search
/ Graphics Plus / Graphics Plus.iso / amiga / plotting / gnuplot3.lzh / gnuplot / readline.c < prev    next >
C/C++ Source or Header  |  1991-09-10  |  12KB  |  562 lines

  1. /* GNUPLOT - readline.c */
  2. /*
  3.  * Copyright (C) 1986, 1987, 1990, 1991   Thomas Williams, Colin Kelley
  4.  *
  5.  * Permission to use, copy, and distribute this software and its
  6.  * documentation for any purpose with or without fee is hereby granted, 
  7.  * provided that the above copyright notice appear in all copies and 
  8.  * that both that copyright notice and this permission notice appear 
  9.  * in supporting documentation.
  10.  *
  11.  * Permission to modify the software is granted, but not the right to
  12.  * distribute the modified code.  Modifications are to be distributed 
  13.  * as patches to released version.
  14.  *  
  15.  * This software is provided "as is" without express or implied warranty.
  16.  * 
  17.  *
  18.  * AUTHORS
  19.  *
  20.  *   Original Software:
  21.  *     Tom Tkacik
  22.  *
  23.  *   Msdos port and some enhancements:
  24.  *     Gershon Elber and many others.
  25.  * 
  26.  * Send your comments or suggestions to 
  27.  *  pixar!info-gnuplot@sun.com.
  28.  * This is a mailing list; to join it send a note to 
  29.  *  pixar!info-gnuplot-request@sun.com.  
  30.  * Send bug reports to
  31.  *  pixar!bug-gnuplot@sun.com.
  32.  */
  33.  
  34. #ifdef READLINE
  35.  
  36. /* a small portable version of GNU's readline */
  37.  
  38. /* do not need any terminal capabilities except backspace,
  39. /* and space overwrites a character */
  40.  
  41. /* NANO-EMACS line editing facility */
  42. /* printable characters print as themselves (insert not overwrite) */
  43. /* ^A moves to the beginning of the line */
  44. /* ^B moves back a single character */
  45. /* ^E moves to the end of the line */
  46. /* ^F moves forward a single character */
  47. /* ^K kills from current position to the end of line */
  48. /* ^P moves back through history */
  49. /* ^N moves forward through history */
  50. /* ^H and DEL delete the previous character */
  51. /* ^D deletes the current character, or EOF if line is empty */
  52. /* ^L/^R redraw line in case it gets trashed */
  53. /* ^U kills the entire line */
  54. /* ^W kills last word */
  55. /* LF and CR return the entire line regardless of the cursor postition */
  56. /* EOF with an empty line returns (char *)NULL */
  57.  
  58. /* all other characters are ignored */
  59.  
  60. #include <stdio.h>
  61. #include <ctype.h>
  62. #include <signal.h>
  63.  
  64. /* SIGTSTP defines job control */
  65. /* if there is job control then we need termios.h instead of termio.h */
  66. #ifdef SIGTSTP
  67. #define TERMIOS
  68. #endif
  69.  
  70.  
  71. #ifndef MSDOS
  72.  
  73. /* UNIX specific stuff */
  74. #ifdef TERMIOS
  75. #include <termios.h>
  76. static struct termios orig_termio, rl_termio;
  77. #else
  78. #include <termio.h>
  79. static struct termio orig_termio, rl_termio;
  80. #endif /* TERMIOS */
  81. static int term_set = 0;    /* =1 if rl_termio set */
  82.  
  83. #else
  84.  
  85. /* MSDOS specific stuff */
  86. #define getc(stdin) msdos_getch()
  87. static char msdos_getch();
  88.  
  89. #endif /* MSDOS */
  90.  
  91.  
  92. /* is it <string.h> or <strings.h>?   just declare what we need */
  93. extern int strlen();
  94. extern char *strcpy();
  95. extern char *malloc();
  96.  
  97. #define MAXBUF    1024
  98. #define BACKSPACE 0x08    /* ^H */
  99. #define SPACE    ' '
  100.  
  101. struct hist {
  102.     char *line;
  103.     struct hist *prev;
  104.     struct hist *next;
  105. };
  106.  
  107. static struct hist *history = NULL;  /* no history yet */
  108. static struct hist *cur_entry = NULL;
  109.  
  110. static char cur_line[MAXBUF];  /* current contents of the line */
  111. static int cur_pos = 0;    /* current position of the cursor */
  112. static int max_pos = 0;    /* maximum character position */
  113.  
  114.  
  115. void add_history();
  116. static void fix_line();
  117. static void redraw_line();
  118. static void clear_line();
  119. static void clear_eoline();
  120. static void copy_line();
  121. static void set_termio();
  122. static void reset_termio();
  123.  
  124. char *
  125. readline(prompt)
  126. char *prompt;
  127. {
  128.  
  129.     char cur_char;
  130.     char *new_line;
  131.  
  132.     /* set the termio so we can do our own input processing */
  133.     set_termio();
  134.  
  135.     /* print the prompt */
  136.     fputs(prompt, stderr);
  137.     cur_line[0] = '\0';
  138.     cur_pos = 0;
  139.     max_pos = 0;
  140.     cur_entry = NULL;
  141.  
  142.     /* get characters */
  143.     for(;;) {
  144.         cur_char = getc(stdin);
  145.         if(isprint(cur_char)) {
  146.             int i;
  147.             for(i=max_pos; i>cur_pos; i--) {
  148.                 cur_line[i] = cur_line[i-1];
  149.             }
  150.             putc(cur_char, stderr);
  151.             cur_line[cur_pos] = cur_char;
  152.             cur_pos += 1;
  153.             max_pos += 1;
  154.             if (cur_pos < max_pos)
  155.                 fix_line();
  156.             cur_line[max_pos] = '\0';
  157.  
  158.         /* else interpret unix terminal driver characters */
  159. #ifdef VERASE
  160.         } else if(cur_char == orig_termio.c_cc[VERASE] ){  /* DEL? */
  161.             if(cur_pos > 0) {
  162.                 int i;
  163.                 cur_pos -= 1;
  164.                 putc(BACKSPACE, stderr);
  165.                 for(i=cur_pos; i<max_pos; i++)
  166.                     cur_line[i] = cur_line[i+1];
  167.                 max_pos -= 1;
  168.                 fix_line();
  169.             }
  170. #endif /* VERASE */
  171. #ifdef VEOF
  172.         } else if(cur_char == orig_termio.c_cc[VEOF] ){   /* ^D? */
  173.             if(max_pos == 0) {
  174.                 reset_termio();
  175.                 return((char *)NULL);
  176.             }
  177.             if((cur_pos < max_pos)&&(cur_char == 004)) { /* ^D */
  178.                 int i;
  179.                 for(i=cur_pos; i<max_pos; i++)
  180.                     cur_line[i] = cur_line[i+1];
  181.                 max_pos -= 1;
  182.                 fix_line();
  183.             }
  184. #endif /* VEOF */
  185. #ifdef VKILL
  186.         } else if(cur_char == orig_termio.c_cc[VKILL] ){  /* ^U? */
  187.             clear_line(prompt);
  188. #endif /* VKILL */
  189. #ifdef VWERASE
  190.         } else if(cur_char == orig_termio.c_cc[VWERASE] ){  /* ^W? */
  191.             while((cur_pos > 0) &&
  192.                   (cur_line[cur_pos-1] == SPACE)) {
  193.                 cur_pos -= 1;
  194.                 putc(BACKSPACE, stderr);
  195.             }
  196.             while((cur_pos > 0) &&
  197.                   (cur_line[cur_pos-1] != SPACE)) {
  198.                 cur_pos -= 1;
  199.                 putc(BACKSPACE, stderr);
  200.             }
  201.             clear_eoline();
  202.             max_pos = cur_pos;
  203. #endif /* VWERASE */
  204. #ifdef VREPRINT
  205.         } else if(cur_char == orig_termio.c_cc[VREPRINT] ){  /* ^R? */
  206.             putc('\n',stderr); /* go to a fresh line */
  207.             redraw_line(prompt);
  208. #else
  209. #ifdef VRPRNT   /* on Ultrix VREPRINT is VRPRNT */
  210.         } else if(cur_char == orig_termio.c_cc[VRPRNT] ){  /* ^R? */
  211.             putc('\n',stderr); /* go to a fresh line */
  212.             redraw_line(prompt);
  213. #endif /* VRPRNT */
  214. #endif /* VREPRINT */
  215. #ifdef VSUSP
  216.         } else if(cur_char == orig_termio.c_cc[VSUSP]) {
  217.             reset_termio();
  218.             kill(0, SIGTSTP);
  219.  
  220.             /* process stops here */
  221.  
  222.             set_termio();
  223.             /* print the prompt */
  224.             redraw_line(prompt);
  225. #endif /* VSUSP */
  226.         } else {
  227.             /* do normal editing commands */
  228.             /* some of these are also done above */
  229.             int i;
  230.             switch(cur_char) {
  231.                 case EOF:
  232.                 reset_termio();
  233.                 return((char *)NULL);
  234.                 case 001: /* ^A */
  235.                 while(cur_pos > 0) {
  236.                     cur_pos -= 1;
  237.                     putc(BACKSPACE, stderr);
  238.                 }
  239.                 break;
  240.                 case 002: /* ^B */
  241.                 if(cur_pos > 0) {
  242.                     cur_pos -= 1;
  243.                     putc(BACKSPACE, stderr);
  244.                 }
  245.                 break;
  246.                 case 005: /* ^E */
  247.                 while(cur_pos < max_pos) {
  248.                     putc(cur_line[cur_pos], stderr);
  249.                     cur_pos += 1;
  250.                 }
  251.                 break;
  252.                 case 006: /* ^F */
  253.                 if(cur_pos < max_pos) {
  254.                     putc(cur_line[cur_pos], stderr);
  255.                     cur_pos += 1;
  256.                 }
  257.                 break;
  258.                 case 013: /* ^K */
  259.                 clear_eoline();
  260.                 max_pos = cur_pos;
  261.                 break;
  262.                 case 020: /* ^P */
  263.                 if(history != NULL) {
  264.                     if(cur_entry == NULL) {
  265.                         cur_entry = history;
  266.                         clear_line(prompt);
  267.                         copy_line(cur_entry->line);
  268.                     } else if(cur_entry->prev != NULL) {
  269.                         cur_entry = cur_entry->prev;
  270.                         clear_line(prompt);
  271.                         copy_line(cur_entry->line);
  272.                     }
  273.                 }
  274.                 break;
  275.                 case 016: /* ^N */
  276.                 if(cur_entry != NULL) {
  277.                     cur_entry = cur_entry->next;
  278.                     clear_line(prompt);
  279.                     if(cur_entry != NULL) 
  280.                         copy_line(cur_entry->line);
  281.                     else
  282.                         cur_pos = max_pos = 0;
  283.                 }
  284.                 break;
  285.                 case 014: /* ^L */
  286.                 case 022: /* ^R */
  287.                 putc('\n',stderr); /* go to a fresh line */
  288.                 redraw_line(prompt);
  289.                 break;
  290.                 case 0177: /* DEL */
  291.                 case 010: /* ^H */
  292.                 if(cur_pos > 0) {
  293.                     cur_pos -= 1;
  294.                     putc(BACKSPACE, stderr);
  295.                     for(i=cur_pos; i<max_pos; i++)
  296.                         cur_line[i] = cur_line[i+1];
  297.                     max_pos -= 1;
  298.                     fix_line();
  299.                 }
  300.                 break;
  301.                 case 004: /* ^D */
  302.                 if(max_pos == 0) {
  303.                     reset_termio();
  304.                     return((char *)NULL);
  305.                 }
  306.                 if(cur_pos < max_pos) {
  307.                     for(i=cur_pos; i<max_pos; i++)
  308.                         cur_line[i] = cur_line[i+1];
  309.                     max_pos -= 1;
  310.                     fix_line();
  311.                 }
  312.                 break;
  313.                 case 025:  /* ^U */
  314.                 clear_line(prompt);
  315.                 break;
  316.                 case 027:  /* ^W */
  317.                 while((cur_pos > 0) &&
  318.                       (cur_line[cur_pos-1] == SPACE)) {
  319.                     cur_pos -= 1;
  320.                     putc(BACKSPACE, stderr);
  321.                 }
  322.                 while((cur_pos > 0) &&
  323.                       (cur_line[cur_pos-1] != SPACE)) {
  324.                     cur_pos -= 1;
  325.                     putc(BACKSPACE, stderr);
  326.                 }
  327.                 clear_eoline();
  328.                 max_pos = cur_pos;
  329.                 break;
  330.                 break;
  331.                 case '\n': /* ^J */
  332.                 case '\r': /* ^M */
  333.                 cur_line[max_pos+1] = '\0';
  334.                 putc('\n', stderr);
  335.                 new_line = malloc(strlen(cur_line)+1);
  336.                 strcpy(new_line,cur_line);
  337.                 reset_termio();
  338.                 return(new_line);
  339.                 default:
  340.                 break;
  341.             }
  342.         }
  343.     }
  344. }
  345.  
  346. /* fix up the line from cur_pos to max_pos */
  347. /* do not need any terminal capabilities except backspace,
  348. /* and space overwrites a character */
  349. static void
  350. fix_line()
  351. {
  352.     int i;
  353.  
  354.     /* write tail of string */
  355.     for(i=cur_pos; i<max_pos; i++)
  356.         putc(cur_line[i], stderr);
  357.  
  358.     /* write a space at the end of the line in case we deleted one */
  359.     putc(SPACE, stderr);
  360.  
  361.     /* backup to original position */
  362.     for(i=max_pos+1; i>cur_pos; i--)
  363.         putc(BACKSPACE, stderr);
  364.  
  365. }
  366.  
  367. /* redraw the entire line, putting the cursor where it belongs */
  368. static void
  369. redraw_line(prompt)
  370. char *prompt;
  371. {
  372.     int i;
  373.  
  374.     fputs(prompt, stderr);
  375.     fputs(cur_line, stderr);
  376.  
  377.     /* put the cursor where it belongs */
  378.     for(i=max_pos; i>cur_pos; i--)
  379.         putc(BACKSPACE, stderr);
  380. }
  381.  
  382. /* clear cur_line and the screen line */
  383. static void
  384. clear_line(prompt)
  385. char *prompt;
  386. {
  387.     int i;
  388.     for(i=0; i<max_pos; i++)
  389.         cur_line[i] = '\0';
  390.  
  391.     for(i=cur_pos; i>0; i--)
  392.         putc(BACKSPACE, stderr);
  393.  
  394.     for(i=0; i<max_pos; i++)
  395.         putc(SPACE, stderr);
  396.  
  397.     putc('\r', stderr);
  398.     fputs(prompt, stderr);
  399.  
  400.     cur_pos = 0;
  401.     max_pos = 0;
  402. }
  403.  
  404. /* clear to end of line and the screen end of line */
  405. static void
  406. clear_eoline(prompt)
  407. char *prompt;
  408. {
  409.     int i;
  410.     for(i=cur_pos; i<max_pos; i++)
  411.         cur_line[i] = '\0';
  412.  
  413.     for(i=cur_pos; i<max_pos; i++)
  414.         putc(SPACE, stderr);
  415.     for(i=cur_pos; i<max_pos; i++)
  416.         putc(BACKSPACE, stderr);
  417. }
  418.  
  419. /* copy line to cur_line, draw it and set cur_pos and max_pos */
  420. static void
  421. copy_line(line)
  422. char *line;
  423. {
  424.     strcpy(cur_line, line);
  425.     fputs(cur_line, stderr);
  426.     cur_pos = max_pos = strlen(cur_line);
  427. }
  428.  
  429. /* add line to the history */
  430. void
  431. add_history(line)
  432. char *line;
  433. {
  434.     struct hist *entry;
  435.     entry = (struct hist *)malloc(sizeof(struct hist));
  436.     entry->line = malloc((unsigned int)strlen(line)+1);
  437.     strcpy(entry->line, line);
  438.  
  439.     entry->prev = history;
  440.     entry->next = NULL;
  441.     if(history != NULL) {
  442.         history->next = entry;
  443.     }
  444.     history = entry;
  445. }
  446.  
  447. #ifdef MSDOS
  448.  
  449. /* Convert Arrow keystrokes to Control characters: */
  450. static  char
  451. msdos_getch()
  452. {
  453.     char c = getch();
  454.  
  455.     if (c == 0) {
  456.     c = getch(); /* Get the extended code. */
  457.     switch (c) {
  458.         case 75: /* Left Arrow. */
  459.         c = 002;
  460.         break;
  461.         case 77: /* Right Arrow. */
  462.         c = 006;
  463.         break;
  464.         case 72: /* Up Arrow. */
  465.         c = 020;
  466.         break;
  467.         case 80: /* Down Arrow. */
  468.         c = 016;
  469.         break;
  470.         case 115: /* Ctl Left Arrow. */
  471.         case 71: /* Home */
  472.         c = 001;
  473.         break;
  474.         case 116: /* Ctl Right Arrow. */
  475.         case 79: /* End */
  476.         c = 005;
  477.         break;
  478.         case 83: /* Delete */
  479.         c = 004;
  480.         break;
  481.         default:
  482.         c = 0;
  483.         break;
  484.     }
  485.     }
  486.     else if (c == 033) { /* ESC */
  487.     c = 025;
  488.     }
  489.  
  490.  
  491.     return c;
  492. }
  493.  
  494. #endif /* MSDOS */
  495.  
  496. /* set termio so we can do our own input processing */
  497. static void
  498. set_termio()
  499. {
  500. #ifndef MSDOS
  501.     if(term_set == 0) {
  502. #ifdef TERMIOS
  503. #ifdef TCGETS
  504.         ioctl(0, TCGETS, &orig_termio);
  505. #else
  506.         tcgetattr(0, &orig_termio);
  507. #endif /* TCGETS */
  508. #else
  509.         ioctl(0, TCGETA, &orig_termio);
  510. #endif /* TERMIOS */
  511.         rl_termio = orig_termio;
  512.  
  513.         rl_termio.c_iflag &= ~(BRKINT|PARMRK|INPCK|IUCLC|IXON|IXOFF);
  514.         rl_termio.c_iflag |=  (IGNBRK|IGNPAR);
  515.  
  516.         rl_termio.c_oflag &= ~(ONOCR);
  517.  
  518.         rl_termio.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL|NOFLSH);
  519.         rl_termio.c_lflag |=  (ISIG);
  520.  
  521.         rl_termio.c_cc[VMIN] = 1;
  522.         rl_termio.c_cc[VTIME] = 0;
  523.  
  524. #ifdef VSUSP
  525.         /* disable suspending process on ^Z */
  526.         rl_termio.c_cc[VSUSP] = 0;
  527. #endif /* VSUSP */
  528.  
  529. #ifdef TERMIOS
  530. #ifdef TCSETSW
  531.         ioctl(0, TCSETSW, &rl_termio);
  532. #else
  533.         tcsetattr(0, TCSADRAIN, &rl_termio);
  534. #endif /* TCSETSW */
  535. #else
  536.         ioctl(0, TCSETAW, &rl_termio);
  537. #endif /* TERMIOS */
  538.         term_set = 1;
  539.     }
  540. #endif /* MSDOS */
  541. }
  542.  
  543. static void
  544. reset_termio()
  545. {
  546. #ifndef MSDOS
  547.     if(term_set == 1) {
  548. #ifdef TERMIOS
  549. #ifdef TCSETSW
  550.         ioctl(0, TCSETSW, &orig_termio);
  551. #else
  552.         tcsetattr(0, TCSADRAIN, &orig_termio);
  553. #endif /* TCSETSW */
  554. #else
  555.         ioctl(0, TCSETAW, &orig_termio);
  556. #endif /* TERMIOS */
  557.         term_set = 0;
  558.     }
  559. #endif /* MSDOS */
  560. }
  561. #endif /* READLINE */
  562.